import java.util.concurrent.TimeUnit;

public class MKSgameLogic implements IGameLogic {
    private int cutOff = 7;
    private int pruneCount = 0;
    
    private int moveCount = 0;
    private long timeTotal = 0;

    private long longestMove = 0;

    private int playerID;
    private int opponentID;
    private GameBoard board;

    public MKSgameLogic() {
        //TODO Write your implementation for this method
    }

    public void initializeGame(int x, int y, int playerID) {
        this.playerID = playerID;
        if (playerID == 1) opponentID = 2;
        else opponentID = 1;
        System.out.println("I am AI player " + playerID + " playing against ?human? player " + opponentID);
        board = new GameBoard(x, y);
    }

    // this method is called after both players turns
    public void insertCoin(int column, int playerID) {
        board.insertCoin(column, playerID);
    }

    private int maxValue(GameBoard boardInstance, int alfa, int beta, int limit) {
        // Test for end state
        // board.printHeuristic();
        if (boardInstance.gameState() != Winner.NOT_FINISHED || limit == 0)
            return boardInstance.heuristic(playerID) - boardInstance.heuristic(opponentID);
        int v = Integer.MIN_VALUE; // "Infinitely" small integer
//        for (GameBoard result : boardInstance.sortedResults(playerID)) {
        //if (boardInstance.sortedResults(playerID).length == 0) System.out.println("error");
        GameBoard[] results = boardInstance.sortedResults(playerID); for (int i = results.length-1; i >= 0; i--) { GameBoard result = results[i];
            v = Math.max(v, minValue(result, alfa, beta, limit - 1));
            if (v >= beta) {
                pruneCount++;
                return v;
            }
            alfa = Math.max(alfa, v);
        }
        return v;
    }

    private int minValue(GameBoard boardInstance, int alfa, int beta, int limit) {
        // Test for end state
        if (boardInstance.gameState() != Winner.NOT_FINISHED || limit == 0)
            return boardInstance.heuristic(playerID) - boardInstance.heuristic(opponentID);
        int v = Integer.MAX_VALUE; // "Infinitely" large integer
//        GameBoard[] results = boardInstance.sortedResults(opponentID); for (int i = results.length-1; i >= 0; i--) { GameBoard result = results[i];
        //if (boardInstance.sortedResults(opponentID).length == 0) System.out.println("error");
        for (GameBoard result : boardInstance.sortedResults(opponentID)) {
           v = Math.min(v, maxValue(result, alfa, beta, limit - 1));
            if (v <= alfa) {
                pruneCount++;
                return v;
            }
            beta = Math.min(beta, v);
        }
        return v;
    }

    public int decideNextMove() {
        long startTime = System.currentTimeMillis();
        int move = -1;
        int maxValue = Integer.MIN_VALUE;
        int moveValue;
        pruneCount = 0;
        for (int action : board.actions()) {
            moveValue = minValue(board.result(action, playerID), Integer.MIN_VALUE, Integer.MAX_VALUE, cutOff);
            System.out.println("Value of move " + action + " = " + moveValue);
            if (moveValue > maxValue) {
                maxValue = moveValue;
                move = action;
            }
        }
        long endTime = System.currentTimeMillis();
        long time = endTime - startTime;
        ++moveCount;
        timeTotal += time;
        if (time > longestMove) longestMove = time;
        System.out.println("Player " + playerID + ": move " + move + " decided in " + TimeUnit.MILLISECONDS.toSeconds(time) + " s. (" + time + "ms) - PruneCount = " + pruneCount);
        System.out.println("Statistics: " + moveCount + " moves of average " + TimeUnit.MILLISECONDS.toSeconds(timeTotal/moveCount) + " s. - longest move: " + TimeUnit.MILLISECONDS.toSeconds(longestMove) + " s.");
        return move;
    }

    public Winner gameFinished() {
        System.out.println("(1) " + board.heuristic(1) + " (2) " + board.heuristic(2));
        board.printHeuristic();
        return board.gameState();
    }
}
